package jpave.query_interface;

import javax.swing.*;

import java.io.File;

import util.Debug.ErrorReport;
import util.ui.CommandLineInputPanel;

import java.awt.*;
import java.awt.event.*;
import java.util.HashMap;
import java.util.Iterator;
import java.util.TreeSet;
import java.util.Vector;
import java.util.prefs.Preferences;

import jpave.dataholders.MainData;

import util.ui.HiddenTabbedPane;
import util.ui.MenuTreeNode;

/*
 * Called from MultiAlignmentPanel, which contains the "Run CAP3" button.
 */
public class CAP3orPhrapTab extends Tab 
{	
	private static final boolean DEBUG = JPaveMain.debug;
	
	public CAP3orPhrapTab ( JPaveFrame inFrame, 
							int inRecordNum, 				
							final String strCAPorPhrap,
							TreeSet<String> inESTs,
							final Vector<String> contigIDs)
	{		
		super(inFrame, null); 				
		
		if(DEBUG) System.err.println("CAP3orPhrapTab");
		
		theESTs = inESTs;
		nRecordNum = inRecordNum; 

		try {
			theParams = inFrame.selectedSession.getCAPparams();
		} catch (Exception e){}
		
		// Create the button panel 
	 	okButton = new JButton ( "Run" );
	 	okButton.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				// If the current state of the dialog input is valid, run phrap
				String strOptions = optionEditPanel.getOptionString();
				if (strOptions != null) {
					try {
						// Save all of the settings to the user preferences
						optionEditPanel.getUserPrefs(userPrefs);
					} catch (Exception err) {
						String s = "Error: with CAP or Phrap";
						System.err.println(s);
						ErrorReport.reportError(err, s);
					}
					// Run PHRAP or CAP
					alignESTs(strCAPorPhrap, contigIDs);
				}
			}
		});
	 	
		if ( strCAPorPhrap.equals( "phrap" ) ) 
		 	resetButton = new JButton ( "Phrap Defaults" );
		else if ( strCAPorPhrap.equals( "cap3" ) ) 
			resetButton = new JButton ( "CAP3 Defaults" );
	 	resetButton.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				optionEditPanel.restoreDefaults();
				optionEditPanel.getOptionString(); // force validation to clear errors
			}
		});
	 	
		cancelButton = new JButton ( "Cancel" );
		cancelButton.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				HiddenTabbedPane parentTabPane = (HiddenTabbedPane) getParent(); 
				parentTabPane.remove(CAP3orPhrapTab.this);
				
				MenuTreeNode node = getParentFrame().menuTree.getNodeWithUserObject(CAP3orPhrapTab.this);
				MenuTreeNode parentNode = node.getParentNode();
				getParentFrame().menuTree.removeNode(node);
				getParentFrame().menuTree.setSelected(parentNode);
				parentTabPane.setSelectedTab( (Tab)parentNode.getUserObject() );
			}
		});
		
		lblTitle = new JLabel(); 
		Font f = lblTitle.getFont();
		lblTitle.setFont(new Font(f.getName(), f.getStyle(), f.getSize()+4));
		lblTitle.setAlignmentX(Component.LEFT_ALIGNMENT);
		txtDesc = new JTextArea();
		txtDesc.setFont(new Font(f.getName(), Font.PLAIN, f.getSize()));
		txtDesc.setAlignmentX(Component.LEFT_ALIGNMENT);
		txtDesc.setEditable(false);

		if ( strCAPorPhrap.equals( "phrap" ) ) 
			doPHRAPSetup ( getParentFrame() );
		else if ( strCAPorPhrap.equals( "cap3" ) ) 
			doCAPSetup ( getParentFrame() );
		else
			throw new RuntimeException ( "Invalid application type of " + strCAPorPhrap );
		
		bottomPanel = new JPanel (  );
		bottomPanel.setLayout ( new BoxLayout ( bottomPanel, BoxLayout.X_AXIS ) );
		bottomPanel.add ( Box.createHorizontalGlue( ) );
		bottomPanel.add ( cancelButton );
		bottomPanel.add ( Box.createHorizontalStrut ( 10 ) );
		bottomPanel.add ( resetButton );
		bottomPanel.add ( Box.createHorizontalStrut ( 10 ) );	

		if (assemblyButton != null) {
			bottomPanel.add ( assemblyButton );						
			bottomPanel.add ( Box.createHorizontalStrut ( 10 ) );
		}

		if ( looseButton != null ) {
			bottomPanel.add( looseButton );
			bottomPanel.add ( Box.createHorizontalStrut ( 10 ) );
			bottomPanel.add( tightButton );
			bottomPanel.add ( Box.createHorizontalStrut ( 10 ) );
		}
		bottomPanel.add ( okButton );
		bottomPanel.add ( Box.createHorizontalGlue ( ) );
		bottomPanel.setMaximumSize( bottomPanel.getPreferredSize() );
		
		// Install everything into the main panel
		setLayout ( new BoxLayout ( this, BoxLayout.Y_AXIS ) );

		setBorder( BorderFactory.createEmptyBorder(10, 10, 10, 10) );
		optionEditPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
		bottomPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
		txtDesc.setMaximumSize(txtDesc.getPreferredSize());
		
		add ( lblTitle );
		add ( txtDesc );
		add ( Box.createVerticalStrut ( 20 ) );	
		add ( optionEditPanel );
		add ( Box.createVerticalStrut ( 30 ) );	
		add ( bottomPanel );
		add ( Box.createVerticalGlue() );
	}
		
	private void alignESTs ( final String strCommand, final Vector<String> contigIDs ) 
	{	
        final InterThreadProgress progress = new InterThreadProgress ( getParentFrame() );
        progress.swapInTabAndStartThread( this, "Running " + strName, 
			new InterThreadProgress.WorkerThread ()
			{
				public void run () throws Throwable
				{
                    progress.setProgressLabel( "Waiting for " + strName + " to finish." );

                    // Run PHRAP or CAP
					MainData cluster =
						getParentFrame().getSettings().executeCAPorPHRAP( strCommand,
														theESTs, 
														optionEditPanel.getOptionString (),
														nRecordNum);
					if ( cluster == null ) {
                        return;
					}

                    progress.swapOutProgressWithClusterTab ( cluster, 
                    		nRecordNum, 
                    		strName);
                    
                    AddNewResult(getID()+2, cluster.writeToFile("/tmp/Contig_", contigIDs, getID()+2));                    
				}
			}
		);
	}
	public String getAlignName ( ) { return strName; }
	
	private void doPHRAPSetup ( final JPaveFrame theFrame ) 
	{
		strName = "PHRAP";
		
		lblTitle.setText("Run PHRAP"); 
		txtDesc.setText("Either change the PHRAP parameters to use.\n" +
				"Or select the 'Phrap Defaults' button to reset the parameters to CAP3 defaults.\n" +
				"Or select the 'Loose Options' button to set the parameters to less stringent values.\n" +
				"Or select the 'Tight Options' button to set the parameters to more stringent values.\n" +
				"Finally, click the 'Run' button to execute PHRAP, or 'Cancel' to abort.");
		
		// Setup the option panel:
		optionEditPanel = new CommandLineInputPanel ( "_param_user_pref" );
		
		optionEditPanel.addIntOptionRow ( "penalty", "(-penalty) Mismatch (substitution) penalty", Integer.MIN_VALUE, 0, -2 );
		optionEditPanel.addIntOptionRow ( "gap_init", "(-gap_init) Gap initiation penalty", Integer.MIN_VALUE, 0, -4 );
		optionEditPanel.addIntOptionRow ( "gap_ext", "(-gap_ext) Gap extension penalty", Integer.MIN_VALUE, 0, -3 );
		optionEditPanel.addIntOptionRow ( "ins_gap_ext", "(-ins_gap_ext) Insertion gap extension penalty", Integer.MIN_VALUE, 0, -3 );
		optionEditPanel.addIntOptionRow ( "del_gap_ext", "(-del_gap_ext) Deletion gap extension penalty", Integer.MIN_VALUE, 0, -3 );
		optionEditPanel.addBooleanOptionRow ( "raw", "(-raw) Use raw rather than complexity-adjusted Smith-Waterman scores", false );
		optionEditPanel.addIntOptionRow ( "minmatch", "(-minmatch) Minimum length of matching word to nucleate SWAT comparison", 0, Integer.MAX_VALUE, 14 );
		optionEditPanel.addIntOptionRow ( "maxmatch", "(-maxmatch) Maximum length of matching word", 0, Integer.MAX_VALUE, 30 );
		optionEditPanel.addIntOptionRow ( "max_group_size", "(-max_group_size) Group size (query file, forward strand words)", 0, Integer.MAX_VALUE, 20 );
		optionEditPanel.addBooleanOptionRow ( "word_raw", "(-word_raw) Use raw rather than complexity-adjusted word length", false );
		optionEditPanel.addIntOptionRow ( "bandwidth", "(-bandwidth) 1/2 band width for banded SWAT searches", -1, Integer.MAX_VALUE, 14 );
		optionEditPanel.addIntOptionRow ( "minscore", "(-minscore) Minimum alignment score", 0, Integer.MAX_VALUE, 30 );
		optionEditPanel.addIntOptionRow ( "vector_bound", "(-vector_bound) Number of potential vector bases at beginning of each read", 0, Integer.MAX_VALUE, 80 );
		optionEditPanel.setMaximumSize( new Dimension ( Integer.MAX_VALUE, (int)optionEditPanel.getPreferredSize().getHeight() ) );
        optionEditPanel.addBooleanOptionRow ( "retain_duplicates", "(-retain_duplicates) Retain exact duplicate reads, rather than eliminating", false );
        optionEditPanel.addIntOptionRow ( "forcelevel", 
                "(-forcelevel) Relaxes stringency to varying degree during final contig merge pass", -1, 11, 0 );
        optionEditPanel.addIntOptionRow ( "bypasslevel", 
                "(-bypasslevel) Bypass inconsistent reads in merge. ( 0 = no bypasses allowed, 1 = single allowed", 
                -1, 2, 1 );
        optionEditPanel.addIntOptionRow ( "maxgap", 
                "(-maxgap) Maximum permitted size of an unmatched region in merging contigs.", 0, Integer.MAX_VALUE, 30 );
        optionEditPanel.addFloatOptionRow ( "repeat_stringency", 
                "(-repeat_stringency) Controls stringency of match required for joins.", 0f, 1f, 0.95f );
        optionEditPanel.addBooleanOptionRow ( "revise_greedy", "(-revise_greedy) Splits initial greedy assembly " +
                "at \"weak joins\", and then reattaches for higher overall score.", false );
        optionEditPanel.addBooleanOptionRow ( "shatter_greedy", "(-shatter_greedy) Breaks assembly at weak joins " +
                "(as with -revise_greedy) but does not try to reattach pieces.", false );
        optionEditPanel.addBooleanOptionRow ( "preassemble", 
                "(-preassemble) Preassemble reads within groups, prior to merging with other groups.", 
                false );
        optionEditPanel.addBooleanOptionRow ( "force_high", 
                "(-force_high) Causes edited high-quality discrepancies to be ignored during final contig merge pass.", 
                false );
    
        // Setup and load preferences
		userPrefs = theFrame.getPreferencesRoot().node( "PHRAP" );
        optionEditPanel.setUserPrefs( userPrefs );
	
        // Setup the loose/tight buttons
	 	looseButton = new JButton ( "Loose Options" );
	 	looseButton.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				Preferences looseOptions = theFrame.getPreferencesRoot().node("PHRAP_LOOSE");
				optionEditPanel.getDefaultPrefs(looseOptions);
				looseOptions.putBoolean(optionEditPanel.createPrefsKey("retain_duplicates"), true);
				looseOptions.putFloat(optionEditPanel.createPrefsKey("repeat_stringency"), 0.95f);
				looseOptions.putBoolean(optionEditPanel.createPrefsKey("revise_greedy"), true);
				looseOptions.putInt(optionEditPanel.createPrefsKey("forcelevel"), 1);
				optionEditPanel.setUserPrefs(looseOptions);
				optionEditPanel.getOptionString(); // force validation to clear errors
			}
		});	
	 	
	 	tightButton = new JButton ( "Tight Options" );
	 	tightButton.addActionListener( new ActionListener () {
	 		public void actionPerformed( ActionEvent e ) {
	 			Preferences tightOptions = theFrame.getPreferencesRoot().node( "PHRAP_TIGHT" );
	 			optionEditPanel.getDefaultPrefs ( tightOptions );
	 			tightOptions.putBoolean( optionEditPanel.createPrefsKey ( "retain_duplicates" ), true );
	 			tightOptions.putFloat( optionEditPanel.createPrefsKey ( "repeat_stringency" ), 0.98f );
	 			tightOptions.putBoolean( optionEditPanel.createPrefsKey ( "revise_greedy" ), true );	 					
	 			tightOptions.putInt( optionEditPanel.createPrefsKey ( "forcelevel" ), 5 );	 
	 			optionEditPanel.setUserPrefs( tightOptions );
	 			optionEditPanel.getOptionString(); // force validation to clear errors
			}
		});
	}
	
	private void doCAPSetup ( JPaveFrame theFrame )
	{
		strName = "CAP3";
		
		lblTitle.setText("Run CAP3");
		txtDesc.setText("Either change the CAP3 parameters to use.\n" +
						"Or select the 'CAP3 Defaults' button to use CAP3 default parameters.\n" +
						"Or select the 'Assembly (ReCAP)' button to use the assembly CAP3 parameters used for ReCAPing.\n" + 
        				"Finally, click the 'Run' button to execute CAP3, or 'Cancel' to abort.");
		
		// Setup the option panel:
		optionEditPanel = new CommandLineInputPanel ( "_param_user_pref" );	
		optionEditPanel.addIntOptionRow ( "a", "(-a) Band expansion size", 10, Integer.MAX_VALUE, 20 );
		optionEditPanel.addIntOptionRow ( "b", "(-b) Base quality cutoff for differences", 15, Integer.MAX_VALUE, 20 );
		optionEditPanel.addIntOptionRow ( "c", "(-c) Base quality cutoff for clipping", 5, Integer.MAX_VALUE, 12 );
		optionEditPanel.addIntOptionRow ( "d", "(-d) Max qscore sum at differences", 20, Integer.MAX_VALUE, 200 );
		optionEditPanel.addIntOptionRow ( "e", "(-e) Clearance between num. of diff", 10, Integer.MAX_VALUE, 30 );
		optionEditPanel.addIntOptionRow ( "f", "(-f) Max gap length in any overlap", 1, Integer.MAX_VALUE, 20 );
		optionEditPanel.addIntOptionRow ( "g", "(-g) Gap penalty factor", 0, Integer.MAX_VALUE, 6 );
		optionEditPanel.addIntOptionRow ( "h", "(-h) Max overhang percent length", 2, Integer.MAX_VALUE, 20 );
		optionEditPanel.addIntOptionRow ( "m", "(-m) Match score factor", 0, Integer.MAX_VALUE, 2 );
		optionEditPanel.addIntOptionRow ( "n", "(-n) Mismatch score factor", Integer.MIN_VALUE, 0, -5 );
		optionEditPanel.addIntOptionRow ( "o", "(-o) Overlap length cutoff", 20, Integer.MAX_VALUE, 40 );
		optionEditPanel.addIntOptionRow ( "p", "(-p) Overlap percent identity cutoff", 65, Integer.MAX_VALUE, 80 );
		optionEditPanel.addIntOptionRow ( "r", "(-r) Reverse orientation value", -1, Integer.MAX_VALUE, 1 );
		optionEditPanel.addIntOptionRow ( "s", "(-s) Overlap similarity score cutoff", 400, Integer.MAX_VALUE, 900 );
		optionEditPanel.addIntOptionRow ( "t", "(-t) Max number of word matches", 30, Integer.MAX_VALUE, 300 );
		optionEditPanel.addIntOptionRow ( "u", "(-u) Min number of constraints for correction", 0, Integer.MAX_VALUE, 3 );
		optionEditPanel.addIntOptionRow ( "v", "(-v) Min number of constraints for linking", 0, Integer.MAX_VALUE, 2 );
		optionEditPanel.addIntOptionRow ( "y", "(-y) Clipping range", 5, Integer.MAX_VALUE, 250 );
		optionEditPanel.addIntOptionRow ( "z", "(-z) Min num. of good reads at clip pos", 0, Integer.MAX_VALUE, 3 );
		optionEditPanel.setMaximumSize( new Dimension ( Integer.MAX_VALUE, (int)optionEditPanel.getPreferredSize().getHeight() ) );
    
        // Setup and load preferences		
		userPrefs = theFrame.getPreferencesRoot().node( "CAP3" );
        optionEditPanel.setUserPrefs( userPrefs );  
 
		assemblyButton = new JButton ( "Assembly (ReCAP)" );
		assemblyButton.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				optionEditPanel.restoreDefaults();
				optionEditPanel.setOptions(theParams);
				optionEditPanel.getOptionString(); 
			}
		});
	    if (theParams == null || theParams.equals(""))
	    		assemblyButton.setEnabled(false);
	    else optionEditPanel.setOptions(theParams);
	}
	

	
	private String strName = null;
	private int nRecordNum = -1; 
	
	private Preferences userPrefs = null;
	private CommandLineInputPanel optionEditPanel;
	private JLabel lblTitle; 	
	private JTextArea txtDesc; 	

	private TreeSet<String> theESTs = null;
	private JPanel bottomPanel;
	private JButton okButton;
	private JButton resetButton;
	private JButton assemblyButton; 	
	private JButton cancelButton;
	private JButton looseButton = null;
	private JButton tightButton;
	private String theParams = null; 
	
	private static HashMap<Integer, String> capOrPhrapResultList = new HashMap<Integer, String>();
	
	static public boolean ResultIDExists(int ID){ return capOrPhrapResultList.containsKey(ID); }
	
	static public boolean AddNewResult(int ID, String fileName)
	{
		//make sure tab not already added
		if(ResultIDExists(ID) || fileName.equals(""))
			return false;
		
		capOrPhrapResultList.put(ID, fileName);
		
		return true;
	}
	
	static public String GetResult(int ID)
	{
		if(ResultIDExists(ID))
			return capOrPhrapResultList.get(ID);
		return "";
	}
	
	//Removes CAP3 or PHRAP result from the list, and deletes the temporary file
	static public boolean RemoveResult(int ID)
	{
		String fileName = "";
		if(!ResultIDExists(ID))
			return false;
		
		fileName = capOrPhrapResultList.get(ID);
		
		File tempFile = new File(fileName);
		tempFile.delete();

		capOrPhrapResultList.remove(ID);
		
		return true;
	}
	
	static public void RemoveAllResults()
	{
		//Execute on shutdown, delete all temp files associated with CAP3/PHRAP results
		int x = 0;
		Vector<Integer> keys= new Vector<Integer> ();
		Iterator<Integer> iter = capOrPhrapResultList.keySet().iterator();
		while(iter.hasNext())
			keys.add((Integer) iter.next());
		
		while(!capOrPhrapResultList.isEmpty())
		{
			RemoveResult(keys.get(x++));
		}			
	}
    
    private static final long serialVersionUID = 1;
}